package {

	import com.hurlant.eval.ByteLoader;
	import com.hurlant.eval.CompiledESC;
	
	
	import flash.display.Sprite;
	import processing.Processing;
	import processing.api.PMath;
	import flash.external.ExternalInterface;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.net.URLRequest;
	import flash.utils.describeType;
	import flash.display.StageQuality;
	import flash.text.*; // ddc
	
	import flash.utils.ByteArray;
	import flash.net.URLRequestHeader;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.geom.ColorTransform;

	import flash.utils.getTimer;

import flash.display.Stage;
import flash.display.StageDisplayState;
import flash.events.*;
import flash.geom.Rectangle;

import flash.errors.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.media.*;
import flash.ui.*;
import flash.utils.*;
import flash.xml.*;

    //import flash.net.navigateToURL;
    //import flash.net.URLRequest;
    //import flash.net.URLVariables;


import flash.system.Capabilities;


	
	
        public class processingAS3Eval extends Sprite {
		
		trace("starting");
		
			private var esc:CompiledESC = new CompiledESC; 
					
			public static var theButton:Sprite = new Sprite();       //ddc

			// this variable flags whether the execution of the sketch should
			// halt. We make the sketch to halt if ta delay is invoked with
			// a ridiculously big number. This is the quickest way to halt a sketch in
			// processing in any specified point (exit doesn't work as well because it
			// closes the output window).
			// We use this for running a set of tests, so that the sketch at some
			// point completely halts at a precise line and stops and we can compare with
			// the same program in processing.
			public static var haltExecution:Boolean = false;

			// see the beginning of the onEnterFrame method in PApplet.as
			// for an explanation of why
			// this variable is needed
			public static var printedSomething:Boolean = false;       //ddc
			public static var printCaseMilliseconds:int;       //ddc

			public static var PRINTSIZE:Boolean = false;       //ddc
			public static var DEBUG:Boolean = false;       //ddc
			public static var TIMEKEEPINGDEBUGTEXT:Boolean = false;       //ddc
			public static var TIMEKEEPING:Boolean = true;       //ddc
			public static var debugTextField:TextField = new TextField();       //ddc
			
			// this is to keep track of all the functions in the processing program
			// if there is no setup function, then we'll add it
			public static var functionNamesmyArray = new Array(); // ddc
			
			public static var lastDateOfRefresh:int = 0;       //ddc
			//public static var dialogShown:Boolean = false;       //ddc
			public static var timeLimit:int = 300;       //ddc
			
			public static var oldw = 0;
			public static var oldh = 0;
			
        
		public static var p:Processing;

// functions to enter and leave full screen mode
function goFullScreen(event:ContextMenuEvent):void
{
   //debugTextField.text = "stage w:" + stage.stageWidth + ", h:" + stage.stageHeight;
   //debugTextField.text += "\rpappl w:" + p.applet.width + ", h:" + p.applet.height;
   //stage.stageWidth = 800;
   //stage.stageHeight=600;

   //stage.fullScreenSourceRect = new Rectangle(0,0,1280,800);
   
   oldw = p.applet.width;
   oldh = p.applet.height;
   //p.applet.y = -200;

    stage.displayState = StageDisplayState.FULL_SCREEN;

	var screenScale = Capabilities.screenResolutionX / Capabilities.screenResolutionY;
	var scale = 0;
	if ((p.applet.width / p.applet.height) >= screenScale) {
		scale =  Capabilities.screenResolutionX / p.applet.width;
		p.applet.width = Capabilities.screenResolutionX;
   		p.applet.height *= scale;	
	}
	else {
		scale =  Capabilities.screenResolutionY / p.applet.height;
   		p.applet.height = Capabilities.screenResolutionY;
		p.applet.width *= scale;
	}

   p.applet.x = -(p.applet.width - oldw)/2;
	p.applet.y = -(p.applet.height - oldh)/2;

}

// An alternate full-screen function that uses hardware scaling to display the upper left corner of the stage in full screen.
function goScaledFullScreen(event:ContextMenuEvent){
   stage.scaleMode = StageScaleMode.NO_BORDER;
   var screenRectangle:Rectangle = new Rectangle(0, 0, 200, 200);
   stage.fullScreenSourceRect = screenRectangle;
   stage.displayState = StageDisplayState.FULL_SCREEN;
}

function exitFullScreen(event:ContextMenuEvent):void
{
   stage.displayState = StageDisplayState.NORMAL;
}

function onFullScreen(fullScreenEvent:FullScreenEvent):void
{
  var bFullScreen=fullScreenEvent.fullScreen;
  if (bFullScreen)
  {
    // do something  here when the display changes to full screen
  }
  else
  {
   	p.applet.width = oldw;
   	p.applet.height = oldh;
   	p.applet.x = 0;
	p.applet.y = 0;
  }

}


// function to enable and disable the context menu items,
// based on what mode we are in.
function menuHandler(event:ContextMenuEvent):void
{
   if (stage.displayState == StageDisplayState.NORMAL)
   {
      event.target.customItems[0].enabled = true;
      event.target.customItems[1].enabled = false;
   }
   else
   {
      event.target.customItems[0].enabled = false;
      event.target.customItems[1].enabled = true;
   }
}


		public function processingAS3Eval() {
		
		trace("miao");

		
			// set stage mode
			stage.scaleMode = StageScaleMode.NO_SCALE;
			//stage.scaleMode = StageScaleMode.SHOW_ALL;
			
			
					debugTextField.text = "Starting stuff "; //ddc
					
			checkForEsc(); 
			
			
			
		}
		
				
				private function checkForEsc():void {
		    debugTextField.text += " checking if ESC is ready ";
			try {
				var compile:Function = getDefinitionByName("ESC::compile") as Function;
			} catch(e:Error) {
     		    debugTextField.text += " ESC not ready yet ";
				setTimeout(checkForEsc, 100);
				return;
			}
			// initialize a few things
		    debugTextField.text += " ESC ready ";

		    APIBridgeClass.debug(" this is to embed the APIBridgeClass class ");
			//run("APIBridgeClass.debug('yayyy');");
			
			
			
			//run("APIBridgeClass.debug('yayyy');");
										
			//run("APIBridgeClass.debug(' processing cointains ' + APIBridgeClass.p);");
			
			
			//this one actually draws a line, how cool is that?
			//run("APIBridgeClass.p.applet.graphics.beginDraw(); APIBridgeClass.p.applet.graphics.pushMatrix(); APIBridgeClass.p.applet.graphics.line(10,10,20,10); APIBridgeClass.p.applet.graphics.popMatrix(); APIBridgeClass.p.applet.graphics.endDraw();");
			
			//run("APIBridgeClass.setAVariable('asasass'); APIBridgeClass.printAVariable();");
			//run("APIBridgeClass.aVariable = 'asasass'; APIBridgeClass.printAVariable();");
			
			//run("namespace _flash_utils = 'flash.utils'; \nuse namespace _flash_utils; \nclass dummy { \npublic static var i:int = 0; \nfunction dummy() { \nAPIBridgeClass.debug('init dummy: '+i); \n} \n} \nnew dummy; \nvar DisplayTrace = function(message){ \nAPIBridgeClass.debug(message); \ndummy.i++; \nAPIBridgeClass.debug('dummy variable is now '+dummy.i); \nAPIBridgeClass.p.applet.graphics.beginDraw(); \nAPIBridgeClass.p.applet.graphics.pushMatrix(); \nAPIBridgeClass.p.applet.graphics.line(dummy.i,10,20,dummy.i); \nAPIBridgeClass.p.applet.graphics.popMatrix(); \nAPIBridgeClass.p.applet.graphics.endDraw();\n} \nID = setInterval(DisplayTrace, 2000, 'Interval called');");
			
			//run("namespace _flash_utils = 'flash.utils'; \nuse namespace _flash_utils; \nfunction myFunc( msg ) { \nAPIBridgeClass.debug( msg ); \nAPIBridgeClass.debug( ' aVariable = ' + APIBridgeClass.aVariable);\n} \niFunc = myFunc; \niFunc( 'Hello world!' );\nAPIBridgeClass.setAVariable(myFunc);\nID = setInterval(myFunc, 2000, 'Interval called');");

			run("namespace _flash_utils = 'flash.utils'; \nuse namespace _flash_utils; \nfunction justAPrintingFunc( msg ) { \nAPIBridgeClass.debug( msg );}\nfunction myFunc( msg ) { \nAPIBridgeClass.debug( msg ); \nAPIBridgeClass.debug( ' aVariable = ' + APIBridgeClass.aVariable);\nif( APIBridgeClass.aVariable != undefined ) \nAPIBridgeClass.aVariable(' this is a call to a function stored in APIBridgeClass ');\n} \niFunc = myFunc; \niFunc( 'Hello world!' );\nAPIBridgeClass.setAVariable(justAPrintingFunc);\nID = setInterval(myFunc, 2000, 'Interval called');");
			
			

		}


		// this whole function by ddc
		function callFromJavaScript(dir):void {			

		  if(dir == 'moreTime')
 		 {
		    timeLimit += 100;
		    lastDateOfRefresh = getTimer();
		    
		    
		    // now, the way to restart a sketch is different whether there is a draw method or not
		    // if there is no draw method, then the sketch is completely stopped and re-started
		    // while if there is a draw method, we keep the sketch and its state and just
		    // keep running the draw method.
		    // In case there is a draw method, we can't just run the sketch directly right here,
		    // we rather neet to set a timer to run it a bit later, and return the call from
		    // the browser (the happened because the user has clicked "continue").
		    // If we were to run the sketch directly here, the thread is still the one of
		    // the call from the javascript user clicking "continue", and in case we have to
		    // give another "slow sketch" notification and hence called the browser, you see that
		    // we have a mess of a thread that started on the browser, went into a slow sketch, and
		    // goes back to the browser. This did cause a problem where the "continue" button
		    // clicks two times, which causes new "slow sketch" notifications to pop-up
		    // and stack-up indefinetely without the user having clicked anything or being able
		    // to stop the stacking-up, and control of the tab is lost.
		    if (!p.applet.context.scope.draw ){
		    	stop();
		    	var timer:Timer = new Timer(250,1);
		    	timer.addEventListener(TimerEvent.TIMER, delayedRun);
		    	timer.start();
		    }
		    else if (p.applet.context.scope.draw ){
		    	p.applet.addAllEventListenersForMouseKeyboardAndEnterFrame();
		    }

		  }
		  if(dir == 'lessTime')
 		 {
		    timeLimit -= 100;
		    lastDateOfRefresh = 0;
		  }

		   if(dir == 'infiniteTime')
		  {
		    timeLimit = 999999999;
		    lastDateOfRefresh = 0;
		  }  
		   if(dir == 'timeKeepingDebugOn')
		  {
		    TIMEKEEPINGDEBUGTEXT = true;
		  } 
		   if(dir == 'timeKeepingDebugOff')
		  {
		    TIMEKEEPINGDEBUGTEXT = false;
		    debugTextField.text = ""; //ddc
		  } 
		}

		public function delayedRun(e:TimerEvent):void{
				    	run(this.script);
		}
		
		public function restart():void {
		    lastDateOfRefresh = 0;
		 	functionNamesmyArray = new Array();
		 	start();
		 	
        }
        
		public function start():void {
		 	
		 	
			// check if we need to reset
			if (p) {
				debugTextField.text += " stopping? ";
				stop();
				}
			// create processing object
			debugTextField.text += " creating processing object ";
			p = new Processing();

			
			APIBridgeClass.p = p;			
			
			// attach sprite to stage
			debugTextField.text += " attach sprite to stage ";
			addChild(p.applet);
			
			// we attach the textfield after the applet so it's on top of it
			addChild(debugTextField); //ddc

			
			debugTextField.text += " adding resize event listener ";
			p.applet.graphics.addEventListener(flash.events.Event.RESIZE, resizeHandler);
			
			//this.addEventListener(Event.ENTER_FRAME, callJs); //ddc
			
			// externalize objects
			debugTextField.text += " externalizing objects ";
			//externalize(p.applet.graphics); //lalala
			//externalize(PMath); //lalala
			
			debugTextField.text += " resetting stage variables ";
			// reset stage variables
			stage.frameRate = 60;
			stage.quality = StageQuality.LOW;

			// call start handler
			//ExternalInterface.call('ProcessingAS.onStart');
			
								
			debugTextField.text += " returning ";

		}
		
		public function stop():void {
			// stop scripts
			p.stop();
			
			// remove sprites
			removeChild(p.applet);
			p.applet.graphics.removeEventListener(flash.events.Event.RESIZE, resizeHandler);
			
			// delete processing object
			p = null;
			
			// call stop handler
			ExternalInterface.call('ProcessingAS.onStop');
		}
		
		public function run(script:String, images:Array = null):void {
		

					debugTextField.text += " start of start ";
	// start script
			start();
			debugTextField.text += " after start ";
			
			// save current script
			this.script = script;
			
			// start method
			if (images && images.length) {
			debugTextField.text += " preloading images? ";
				// preload images
				preloadStack = images;
				preloadImages();
			} else {
			debugTextField.text += " parsing and evaluating ";
				// start immediately
				parseAndEvaluate();
			}

			debugTextField.text += " end of run method ";
			
		}
		
		private var script:String = '';
		
		private function parseAndEvaluate():void {
			// evaluate code
						
			//lalala took away a lot of stuff here

			try {
				debugTextField.text += "about to compile and load: ";
				var bytes:ByteArray = esc.compile(script);
				ByteLoader.loadBytes(bytes, true);
			} catch (e:Error) {
				debugTextField.text += "Error in eval: "+e;
				throw e;
			}			
			p.start();
		}
		
		public function resizeHandler(e:Event):void {
			// dispatch resize handler
			ExternalInterface.call('ProcessingAS.onResize', p.applet.graphics.width, p.applet.graphics.height);
			// move sprite
			x = -(p.applet.graphics.width / 2);
			y = -(p.applet.graphics.height / 2);
		}
		
		private var preloadStack:Array = [];
		private var preloader:Loader;
		
		public function preloadImages():void {
			// check that there is an image to be loaded
			if (!preloadStack.length) {
				// done preloading
				parseAndEvaluate();
				return;
			}
		
			// load image path
			preloader.load(new URLRequest(preloadStack[preloadStack.length - 1][1]));
		}
		
		private function preloaderHandler(e:Event):void {
			// pop stack and save preloaded image
			var path:Array = preloadStack.pop();
			var image:BitmapData = new BitmapData(preloader.content.width, preloader.content.height);
			image.draw(preloader.content);
			p.applet.loadImage(path[0], image);
			
			// preload next image
			preloadImages();
		}
		
		private function externalize(obj:Object):void {
			// add callbacks
			var description:XML = describeType(obj);
			for each (var method:String in description..method.(@declaredBy==description.@name).@name)
				ExternalInterface.addCallback(method, obj[method]);
		}
	}
}
